﻿using FastApi.Attributes;
using Owin;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Threading.Tasks;
using Newtonsoft.Json;
using System.IO;

namespace FastApi
{
    public class FastApp
    {
        #region 静态变量

        public readonly Dictionary<string, FastModule> FastModuleDict = new();

        #endregion

        #region 变量

        private readonly IAppBuilder webApp;

        internal readonly Dictionary<RuntimeTypeHandle, FastParamCreator> ParamCreatorDict = new();

        internal readonly List<FastFilter> FilterList = new();

        #endregion

        #region 路由匹配

        /// <summary>
        /// 路由匹配
        /// </summary>
        /// <param name="route"></param>
        private void MapRoute(string route)
        {
            webApp.MapWhen(ctx =>
            {
                if (ctx.Request.Path.HasValue)
                {
                    return ctx.Request.Path.Value.StartsWith(route, StringComparison.OrdinalIgnoreCase);
                }
                return false;

            }, builder =>
            {
                builder.Run(async context =>
                {
                    var module = FastModuleDict[route];
                    string path = context.Request.Path.ToString();
                    var name = path.Substring(route.Length);

                    #region 404

                    if (string.IsNullOrEmpty(name))
                    {
                        context.Response.StatusCode = 404;
                        return;
                    }

                    var ok = module.ActionDict.TryGetValue(name.ToLower(), out var action);
                    if (!ok)
                    {
                        context.Response.StatusCode = 404;
                        return;
                    }

                    #endregion

                    #region 405

                    if (action.HttpMethod != context.Request.Method)
                    {
                        context.Response.StatusCode = 405;
                        return;
                    }

                    #endregion

                    #region 计时器

                    Stopwatch sw = null;
                    if (LogTime || module.LogTime || action.LogTime)
                    {
                        sw = new Stopwatch();
                        sw.Start();
                    }

                    #endregion

                    var fastCtx = new FastContext(context, module, action);

                    object[] moduleData = null;

                    object[] actionData = null;

                    try
                    {
                        #region 过滤器

                        if (FilterList.Count > 0)
                        {
                            if (!(module.ClearFilter || action.ClearFilter))
                            {
                                foreach (var item in FilterList)
                                {
                                    if (item.Execute != null)
                                    {
                                        ok = item.Execute(fastCtx);
                                    }
                                    else
                                    {
                                        ok = await item.ExecuteAsync(fastCtx);
                                    }

                                    if (!ok)
                                    {
                                        break;
                                    }
                                }
                            }
                        }

                        if (!ok)
                        {
                            return;
                        }

                        #endregion

                        #region 赋值参数

                        if (module.ParamList.Count > 0)
                        {
                            moduleData = await FastUtils.CreateParamList(fastCtx, 0);
                        }

                        if (action.ParamList.Count > 0)
                        {
                            actionData = await FastUtils.CreateParamList(fastCtx, 1);
                            fastCtx.RequestData = actionData;
                        }

                        #endregion

                        #region 执行方法

                        object methodVal;

                        //静态方法
                        if (action.IsStatic)
                        {
                            if (action.ParamList.Count == 0) //无参数
                            {
                                methodVal = action.Invoker(module.Type);
                            }
                            else //有参数
                            {
                                methodVal = action.Invoker(module.Type, actionData);
                            }
                        }
                        else //非静态方法
                        {
                            object classObj;
                            if (module.ParamList.Count == 0) //无参数
                            {
                                classObj = module.Invoker();
                            }
                            else //有参数
                            {
                                classObj = module.Invoker(moduleData);
                            }

                            if (action.ParamList.Count == 0) //无参数
                            {
                                methodVal = action.Invoker(classObj);
                            }
                            else //有参数
                            {
                                methodVal = action.Invoker(classObj, actionData);
                            }

                        }

                        #endregion

                        #region 方法返回值Task处理

                        if (action.ActionTypeTask == FastActionTypeTask.Default)
                        {
                            fastCtx.ResponseData = methodVal;
                        }
                        else if (action.ActionTypeTask == FastActionTypeTask.Task)
                        {
                            await (Task)methodVal;
                        }
                        else
                        {
                            var task = (Task)methodVal;
                            await task;
                            fastCtx.ResponseData = FastEmit.PropertyExtensions.GetPropertyValue(task, "Result");
                        }

                        #endregion

                        #region 提交事务

                        //提交类参数事务
                        if (module.IsTran)
                        {
                            for (int i = 0; i < module.ParamList.Count; i++)
                            {
                                var par = module.ParamList[i];
                                if (par.IsTran)
                                {
                                    if (par.Creator.CommitAsync != null)
                                    {
                                        await par.Creator.CommitAsync.Invoke(fastCtx, moduleData[i]);
                                    }
                                    else
                                    {
                                        par.Creator.Commit.Invoke(fastCtx, moduleData[i]);
                                    }
                                }
                            }
                        }

                        //提交方法参数事务
                        if (action.IsTran)
                        {
                            for (int i = 0; i < action.ParamList.Count; i++)
                            {
                                var par = action.ParamList[i];
                                if (par.IsTran)
                                {
                                    if (par.Creator.CommitAsync != null)
                                    {
                                        await par.Creator.CommitAsync.Invoke(fastCtx, actionData[i]);
                                    }
                                    else
                                    {
                                        par.Creator.Commit.Invoke(fastCtx, actionData[i]);
                                    }
                                }
                            }
                        }

                        #endregion

                        #region 计算时间

                        if (sw != null)
                        {
                            sw.Stop();
                            fastCtx.ExecuteTime = sw.ElapsedMilliseconds;
                        }

                        #endregion

                    }
                    catch (Exception ex)
                    {
                        #region 回滚事务

                        //回滚类参数事务
                        if (moduleData != null && module.IsTran)
                        {
                            for (int i = 0; i < module.ParamList.Count; i++)
                            {
                                var par = module.ParamList[i];
                                if (par.IsTran)
                                {
                                    if (par.Creator.RollbackAsync != null)
                                    {
                                        await par.Creator.RollbackAsync.Invoke(fastCtx, moduleData[i]);
                                    }
                                    else
                                    {
                                        par.Creator.Rollback.Invoke(fastCtx, moduleData[i]);
                                    }

                                }
                            }
                        }

                        //回滚方法参数事务
                        if (actionData != null && action.IsTran)
                        {
                            for (int i = 0; i < action.ParamList.Count; i++)
                            {
                                var par = action.ParamList[i];
                                if (par.IsTran)
                                {
                                    if (par.Creator.RollbackAsync != null)
                                    {
                                        await par.Creator.RollbackAsync.Invoke(fastCtx, actionData[i]);
                                    }
                                    else
                                    {
                                        par.Creator.Rollback.Invoke(fastCtx, actionData[i]);
                                    }
                                }
                            }
                        }

                        #endregion

                        #region 计算时间

                        if (sw != null)
                        {
                            sw.Stop();
                            fastCtx.ExecuteTime = sw.ElapsedMilliseconds;
                        }

                        #endregion

                        if (OnException != null)
                        {
                            context.Response.ContentType = "application/json; charset=utf-8";
                            await OnException.Invoke(fastCtx, ex);
                        }
                        return;
                    }
                    finally
                    {
                        #region 释放资源

                        //释放类参数
                        if (moduleData != null && module.NeedDispose)
                        {
                            for (int i = 0; i < module.ParamList.Count; i++)
                            {
                                var par = module.ParamList[i];
                                if (par.NeedDispose)
                                {
                                    if (par.Creator.DisposeAsync != null)
                                    {
                                        await par.Creator.DisposeAsync.Invoke(fastCtx, moduleData[i]);
                                    }
                                    else
                                    {
                                        par.Creator.Dispose.Invoke(fastCtx, moduleData[i]);
                                    }
                                }
                            }
                        }

                        //释放方法参数
                        if (actionData != null && action.NeedDispose)
                        {
                            for (int i = 0; i < action.ParamList.Count; i++)
                            {
                                var par = action.ParamList[i];
                                if (par.NeedDispose)
                                {
                                    if (par.Creator.DisposeAsync != null)
                                    {
                                        await par.Creator.DisposeAsync.Invoke(fastCtx, actionData[i]);
                                    }
                                    else
                                    {
                                        par.Creator.Dispose.Invoke(fastCtx, actionData[i]);
                                    }
                                }
                            }
                        }

                        #endregion
                    }

                    sw?.Restart();

                    try
                    {
                        #region 响应处理

                        if (action.ActionType == FastActionType.Default) //默认
                        {
                            context.Response.ContentType = "application/json; charset=utf-8";
                            //开始响应前
                            if (OnBeforeResponse != null)
                            {
                                await OnBeforeResponse(fastCtx);
                            }
                            else
                            {
                                var jsonString = JsonConvert.SerializeObject(fastCtx.ResponseData);
                                await context.Response.WriteAsync(jsonString);
                            }
                        }
                        else if (action.ActionType == FastActionType.Redirect) //跳转
                        {
                            context.Response.Redirect((string)fastCtx.ResponseData);
                        }
                        else if (action.ActionType == FastActionType.File) //文件
                        {
                            var file = (FastFile)fastCtx.ResponseData;
                            if (string.IsNullOrEmpty(file.FilePath))
                            {
                                await context.Response.WriteFileAsync(file.FileBytes, file.FileName);
                            }
                            else
                            {
                                await context.Response.WriteFileAsync(file.FilePath, file.FileName);
                            }
                        }
                        else if (action.ActionType == FastActionType.View) //视图
                        {
                            if (OnView != null)
                            {
                                var view = (FastView)fastCtx.ResponseData;
                                await OnView.Invoke(fastCtx, view);
                            }
                        }
                        //FastActionType.Customer 不需要处理

                        #endregion

                        #region 计算时间

                        if (sw != null)
                        {
                            sw.Stop();
                            fastCtx.JsonSerialTime = sw.ElapsedMilliseconds;
                        }

                        #endregion
                    }
                    catch (Exception ex)
                    {
                        #region 计算时间

                        if (sw != null)
                        {
                            sw.Stop();
                            fastCtx.JsonSerialTime = sw.ElapsedMilliseconds;
                        }

                        #endregion

                        if (OnException != null)
                        {
                            context.Response.ContentType = "application/json; charset=utf-8";
                            await OnException.Invoke(fastCtx, ex);
                        }
                        return;
                    }

                    #region 响应结束后

                    if (OnAfterResponse != null)
                    {
                        OnAfterResponse.Invoke(fastCtx);
                    }
                    else if (OnAfterResponseAsync != null)
                    {
                        await OnAfterResponseAsync(fastCtx);
                    }

                    #endregion
                });
            });
        }

        #endregion

        /// <summary>
        /// 记录运行时间
        /// </summary>
        public bool LogTime { get; set; }

        public string BaseDirectory { get; }

        public bool IsDevelopment { get; }

        public bool IsProduction { get; }

        public string ContentRootPath { get; }

        public string WebRootPath { get; }

        public FastApp(IAppBuilder webApp)
        {
            this.webApp = webApp;
            BaseDirectory = AppDomain.CurrentDomain.BaseDirectory;
            if (Debugger.IsAttached) //调试环境
            {
                IsDevelopment = true;
                IsProduction = false;
                ContentRootPath = Directory.GetParent(BaseDirectory).Parent.Parent.FullName + Path.DirectorySeparatorChar;
            }
            else //生产环境
            {
                IsDevelopment = false;
                IsProduction = true;
                ContentRootPath = BaseDirectory;
            }
            WebRootPath = ContentRootPath + "wwwroot" + Path.DirectorySeparatorChar;
        }

        /// <summary>
        /// 添加参数
        /// </summary>
        /// <param name="type"></param>
        /// <param name="creator"></param>
        public void AddParams(Type type, FastParamCreator creator)
        {
            ParamCreatorDict.Add(type.TypeHandle, creator);
        }

        /// <summary>
        /// 添加过滤器
        /// </summary>
        /// <param name="filter"></param>
        public void AddFilter(FastFilter filter)
        {
            FilterList.Add(filter);
        }

        /// <summary>
        /// 添加路由
        /// </summary>
        /// <param name="route"></param>
        /// <param name="type"></param>
        public void AddRoute(string route, Type type)
        {
            if (!route.StartsWith("/"))
            {
                route = "/" + route;
            }
            if (!route.EndsWith("/"))
            {
                route += "/";
            }

            route = route.ToLower();
            FastUtils.AddType(route, type, this);
            MapRoute(route);
        }

        /// <summary>
        /// 添加路由
        /// </summary>
        /// <param name="type"></param>
        /// <param name="prefix"></param>
        public void AddRoute(Type type, string prefix = "")
        {
            var routeAttr = type.GetCustomAttribute<FastRouteAttribute>(false);
            if (routeAttr == null)
            {
                throw new Exception($"FastRouteAttribute not define at {type.Name}");
            }
            string route = routeAttr.Name;
            if (!route.StartsWith("/"))
            {
                route = "/" + route;
            }
            if (!route.EndsWith("/"))
            {
                route += "/";
            }
            if (!string.IsNullOrEmpty(prefix))
            {
                if (!prefix.StartsWith("/"))
                {
                    prefix = "/" + prefix;
                }
                if (prefix.EndsWith("/"))
                {
                    prefix = prefix.Substring(0, prefix.LastIndexOf("/"));
                }
            }
            route = prefix + route;
            AddRoute(route, type);
        }

        /// <summary>
        /// 添加路由Type[]
        /// </summary>
        /// <param name="types"></param>
        /// <param name="prefix"></param>
        public void AddRoute(Type[] types, string prefix = "")
        {
            foreach (var type in types)
            {
                var routeAttr = type.GetCustomAttribute<FastRouteAttribute>(false);
                if (routeAttr == null)
                {
                    continue;
                }
                string route = routeAttr.Name;
                if (!route.StartsWith("/"))
                {
                    route = "/" + route;
                }
                if (!route.EndsWith("/"))
                {
                    route += "/";
                }
                if (!string.IsNullOrEmpty(prefix))
                {
                    if (!prefix.StartsWith("/"))
                    {
                        prefix = "/" + prefix;
                    }
                    if (prefix.EndsWith("/"))
                    {
                        prefix = prefix.Substring(0, prefix.LastIndexOf("/"));
                    }
                }
                route = prefix + route;
                AddRoute(route, type);
            }
        }

        /// <summary>
        ///  添加程序集路由
        /// </summary>
        /// <param name="assambly"></param>
        /// <param name="prefix"></param>
        public void AddRoute(Assembly assambly, string prefix = "")
        {
            AddRoute(assambly.GetTypes(), prefix);
        }

        /// <summary>
        /// 开始响应前
        /// </summary>
        public Func<FastContext, Task> OnBeforeResponse { get; set; }

        /// <summary>
        /// 响应结束后
        /// </summary>
        public event Action<FastContext> OnAfterResponse;

        /// <summary>
        /// 响应结束后（异步）
        /// </summary>
        public event Func<FastContext, Task> OnAfterResponseAsync;

        /// <summary>
        /// 异常事件
        /// </summary>
        public event Func<FastContext, Exception, Task> OnException;

        /// <summary>
        /// 视图事件
        /// </summary>
        public event Func<FastContext, FastView, Task> OnView;

    }
}
